package cn.clx.ymcc.service.impl;

import cn.clx.ymcc.domain.*;
import cn.clx.ymcc.dto.CourseOrderDto;
import cn.clx.ymcc.dto.KillCourseOrderCreateParamsDto;
import cn.clx.ymcc.dto.KillPreOrderParamsDto;
import cn.clx.ymcc.dto.PayDto;
import cn.clx.ymcc.feign.ICourseUserLearnFeignClient;
import cn.clx.ymcc.mapper.CourseOrderMapper;
import cn.clx.ymcc.result.JSONResult;
import cn.clx.ymcc.service.ICourseOrderItemService;
import cn.clx.ymcc.service.ICourseOrderService;
import cn.clx.ymcc.util.AssertUtil;
import cn.clx.ymcc.util.CodeGenerateUtils;
import cn.clx.ymcc.vo.CourseInfo;
import cn.clx.ymcc.vo.OrderCourseInfoVo;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author clx
 * @since 2022-07-29
 */
@Service
@Slf4j
public class CourseOrderServiceImpl extends ServiceImpl<CourseOrderMapper, CourseOrder> implements ICourseOrderService {

    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;
    @Autowired
    private ICourseUserLearnFeignClient courseFeignClient;
    @Autowired
    private ICourseOrderItemService courseOrderItemService;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    /**
     * 课程下单
     * @param dto
     */
    @Override
    @Transactional
    public String placeAnOrder(CourseOrderDto dto) {
        //1.校验参数
        //业务校验
      String join = StringUtils.join(dto.getCourseIds(), ",");
        Long LoginId = 3l;
        String key =LoginId+":"+ join;
        Object redisTem = redisTemplate.opsForValue().get(key);
        AssertUtil.isNotNull(redisTem,"token过期，请重新下单");
        AssertUtil.isEquals(dto.getToken(),redisTem.toString(),"非法请求，请重新下单");
        //保存订单
        JSONResult jsonResult = courseFeignClient.getOrderCourseinfo(join);
        AssertUtil.isTrue(jsonResult.isSuccess(),"服务出错，请稍后再试");
        AssertUtil.isNotNull(jsonResult.getData(),"服务不可达,请稍后再试");
        String result = JSON.toJSONString(jsonResult.getData());
        OrderCourseInfoVo orderCourseInfoVo = JSON.parseObject(result, OrderCourseInfoVo.class);



        List<CourseInfo> courseInfos = orderCourseInfoVo.getCourseInfos();
        Date date = new Date();

        //保存主订单
        CourseOrder courseOrder = new CourseOrder();
        courseOrder.setCreateTime(date);
        courseOrder.setOrderNo(CodeGenerateUtils.generateOrderSn(LoginId));
        courseOrder.setTotalAmount(orderCourseInfoVo.getTotalAmount());
        courseOrder.setTotalCount(1);
        courseOrder.setStatusOrder(PayOrder.STATE_WAIT_PAY); //支付状态为 待支付
        // @TODO  登录之后后面做，现在写死
        courseOrder.setUserId(LoginId);
        courseOrder.setVersion(0);
        courseOrder.setPayType(dto.getPayType());
        //insert(courseOrder);
        StringBuilder sb = new StringBuilder("课程下单【");
        courseInfos.forEach(courseInfo -> {
            //保存子订单
            Course course = courseInfo.getCourse();
            CourseMarket courseMarket = courseInfo.getCourseMarket();
            CourseOrderItem item = new CourseOrderItem();
            //item.setOrderId(courseOrder.getId());
            item.setAmount(courseMarket.getPrice());
            item.setCount(1);
            item.setCreateTime(date);
            item.setCourseId(course.getId());
            item.setCourseName(course.getName());
            item.setCoursePic(course.getPic());
            item.setVersion(0);
            item.setOrderNo(courseOrder.getOrderNo());
            //courseOrderItemService.insert(item);
            sb.append(course.getName()+",");
            //主订单中添加子订单
            courseOrder.getCourseOrderItems().add(item);
        });
        sb.append("】");
        //设置主订单标题
        courseOrder.setTitle(sb.toString());
        //updateById(courseOrder);


        PayDto payDto = new PayDto(
                courseOrder.getTotalAmount(),
                courseOrder.getPayType(),
                null,
                courseOrder.getOrderNo(),
                courseOrder.getUserId(),
                key,
                courseOrder.getTitle()
        );
        Message<String> message = MessageBuilder.withPayload(JSON.toJSONString(payDto)).build();
        /**
         * message:传递到pay_order 表中的数据 ，----消息主体 要传递到另外的微服务的
         * arg:传递到事务mq中的对象，做本地事务操作 -----不传递，只是在微服务中进行事务的提价
         */
        TransactionSendResult transactionSendResult = rocketMQTemplate.sendMessageInTransaction(
                "service-order-course-pay-producer",
                "course-order-topic:course-order-tags",
                message,
                courseOrder
        );
        //发送状态
        SendStatus sendStatus = transactionSendResult.getSendStatus();

        boolean sendOK = sendStatus == SendStatus.SEND_OK;
        AssertUtil.isTrue(sendOK,"下单失败");
        LocalTransactionState localTransactionState = transactionSendResult.getLocalTransactionState();
        boolean LocalOK = localTransactionState == LocalTransactionState.COMMIT_MESSAGE;
        AssertUtil.isTrue(LocalOK,"消息发送失败");

        //这里还要发送一个超时支付的mq消息=============

        try {
            SendResult sendResult = rocketMQTemplate.syncSend("pay-timeout-topic:pay-timeout-tags",
                    MessageBuilder.withPayload(courseOrder.getOrderNo()).build(), //只传递订单
                    3000,//发送mq消息 超时时间
                    4);//设置延迟级别

            SendStatus payTimeOutsendStatus = sendResult.getSendStatus();
            boolean sendIsOK = payTimeOutsendStatus == SendStatus.SEND_OK;
            AssertUtil.isTrue(sendIsOK,"发送超时取消订单失败");
        } catch (Exception e) {
            e.printStackTrace();
            //治理发送失败，我们就要尝试补偿机制了
            /**
             * 1.重试发送机制
             * 2.记录日志
             * 3.发送消息给相关人员，人为处理
             */
        }

        //删除redis中的值----防重token 重复下单
        redisTemplate.delete(key);
        return courseOrder.getOrderNo();

    }

    /**
     * 基于订单编号查询
     * @param orderNo
     * @return
     */
    @Override
    public CourseOrder findOrderByOrderNo(String orderNo) {
        Wrapper<CourseOrder> wrapper= new EntityWrapper<>();
        wrapper.eq("order_no",orderNo);
        CourseOrder order = selectOne(wrapper);
        return order;
    }
    /**
     * 秒杀课程提交订单返回订单号
     * @param dto
     * @return
     */
    @Override
    public String killPlaceOrder(KillCourseOrderCreateParamsDto dto) {
        //1.校验参数
        //业务校验
        String orderNo = dto.getOrderNo();
        KillPreOrderParamsDto preOrder = (KillPreOrderParamsDto) redisTemplate.opsForValue().get(orderNo);
        AssertUtil.isNotNull(preOrder,"网络繁忙，请稍后再试");
        Long LoginId = 3l;
        String key =LoginId+":"+ preOrder.getCourseId();
        Object redisTem = redisTemplate.opsForValue().get(key);
        AssertUtil.isNotNull(redisTem,"token过期，请重新下单");
        AssertUtil.isEquals(dto.getToken(),redisTem.toString(),"非法请求，请重新下单");
        //保存订单
        Date date = new Date();
        //保存主订单
        CourseOrder courseOrder = new CourseOrder();
        courseOrder.setCreateTime(date);
        courseOrder.setOrderNo(dto.getOrderNo());
        courseOrder.setTotalAmount(preOrder.getKillAmount());
        courseOrder.setTotalCount(1);
        courseOrder.setStatusOrder(PayOrder.STATE_WAIT_PAY); //支付状态为 待支付
        // @TODO  登录之后后面做，现在写死
        courseOrder.setUserId(LoginId);
        courseOrder.setVersion(0);
        courseOrder.setPayType(dto.getPayType());
        courseOrder.setTitle(preOrder.getCourseName());
        //insert(courseOrder);

        //保存子订单
        CourseOrderItem item = new CourseOrderItem();
        //item.setOrderId(courseOrder.getId());
        item.setAmount(preOrder.getKillAmount());
        item.setCount(preOrder.getKillCount());
        item.setCreateTime(date);
        item.setCourseId(preOrder.getCourseId());
        item.setCourseName(preOrder.getCourseName());
        item.setCoursePic(preOrder.getCoursePic());
        item.setVersion(0);
        item.setOrderNo(preOrder.getOrderNo());
        //courseOrderItemService.insert(item);

        //主订单中添加子订单
        courseOrder.getCourseOrderItems().add(item);


        PayDto payDto = new PayDto(
                courseOrder.getTotalAmount(),
                courseOrder.getPayType(),
                null,
                courseOrder.getOrderNo(),
                courseOrder.getUserId(),
                key,
                courseOrder.getTitle()
        );
        Message<String> message = MessageBuilder.withPayload(JSON.toJSONString(payDto)).build();
        /**
         * message:传递到pay_order 表中的数据 ，----消息主体 要传递到另外的微服务的
         * arg:传递到事务mq中的对象，做本地事务操作 -----不传递，只是在微服务中进行事务的提价
         */
        TransactionSendResult transactionSendResult = rocketMQTemplate.sendMessageInTransaction(
                "service-order-course-pay-producer",
                "course-order-topic:course-order-tags",
                message,
                courseOrder
        );
        //发送状态
        SendStatus sendStatus = transactionSendResult.getSendStatus();

        boolean sendOK = sendStatus == SendStatus.SEND_OK;
        AssertUtil.isTrue(sendOK,"下单失败");
        LocalTransactionState localTransactionState = transactionSendResult.getLocalTransactionState();
        boolean LocalOK = localTransactionState == LocalTransactionState.COMMIT_MESSAGE;
        AssertUtil.isTrue(LocalOK,"消息发送失败");

        //这里还要发送一个超时支付的mq消息=============

        try {
            SendResult sendResult = rocketMQTemplate.syncSend("pay-timeout-topic:pay-timeout-tags",
                    MessageBuilder.withPayload(courseOrder.getOrderNo()).build(), //只传递订单
                    3000,//发送mq消息 超时时间
                    5);//设置延迟级别

            SendStatus payTimeOutsendStatus = sendResult.getSendStatus();
            boolean sendIsOK = payTimeOutsendStatus == SendStatus.SEND_OK;
            AssertUtil.isTrue(sendIsOK,"发送超时取消订单失败");
        } catch (Exception e) {
            e.printStackTrace();
            //治理发送失败，我们就要尝试补偿机制了
            /**
             * 1.重试发送机制
             * 2.记录日志
             * 3.发送消息给相关人员，人为处理
             */
        }

        //删除redis中的值----防重token 重复下单
        redisTemplate.delete(key);
        return courseOrder.getOrderNo();

    }
}
